package com.github.hwangrq.minicat.startup;

import com.github.hwangrq.minicat.classloader.MyClassLoader;
import com.github.hwangrq.minicat.container.*;
import com.github.hwangrq.minicat.server.RequestProcessor;
import com.github.hwangrq.minicat.servlet.HttpServlet;
import com.github.hwangrq.minicat.servlet.Servlet;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author hwangrq
 * @date 2021/3/16 22:01
 * @description
 */
public class Bootstrap {

    /** 定义socket监听的端口号 */
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    private Server server = new Server();

    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void start() throws Exception {
        loadServer();

        // 定义线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

        ServerSocket serverSocket = new ServerSocket(port);
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, server);
            threadPoolExecutor.execute(requestProcessor);
        }
    }

    /**
     * 加载解析server.xml
     * <Server>
     *     <Service name="Catalina">
     *         <Connector port="80"/>
     *         <Engine>
     *             <Host name="localhost" appBase="E:\00code\00lagou\00Homework\tomcat-homework\webapps" />
     *         </Engine>
     *     </Service>
     * </Server>
     */
    private void loadServer() {

        InputStream resourceAsStream = Bootstrap.class.getClassLoader().getResourceAsStream("server.xml");
        try {
            Document document = new SAXReader().read(resourceAsStream);
            Element rootElement = document.getRootElement();

            Element connectorElement = (Element) rootElement.selectSingleNode("//Connector");
            String port = connectorElement.attributeValue("port");
            this.port = Integer.parseInt(port);

            List<Element> serviceList = rootElement.selectNodes("//Service");
            for (Element service : serviceList) {
                String serviceName = service.attributeValue("name");
                Mapper mapper = new Mapper();
                Host host = new Host();

                List<Element> hostElementList = rootElement.selectNodes("//Host");
                for (Element hostElement : hostElementList) {
                    String name = hostElement.attributeValue("name");
                    String appBase = hostElement.attributeValue("appBase");
                    File file = new File(appBase);
                    String[] fileList = file.list();
                    if (!file.exists() || fileList == null) {
                        System.err.println("appBase error");
                        break;
                    }
                    //遍历子文件夹，即：实际的项目列表
                    for (String filename : fileList) {
                        //将项目封装成 context，并保存入host
                        Context context = loadContextServlet(appBase, filename);
                        if (context != null) {
                            host.addContext(context);
                        }
                    }
                    host.setName(name);
                    mapper.addHost(host);
                }
                server.putService(serviceName, mapper);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * <web-app>
     *     <servlet>
     *         <servlet-name>one</servlet-name>
     *         <servlet-class>com.github.hwangrq.servlet.MyServlet</servlet-class>
     *     </servlet>
     *
     *     <servlet-mapping>
     *         <servlet-name>one</servlet-name>
     *         <url-pattern>/one</url-pattern>
     *     </servlet-mapping>
     * </web-app>
     */
    private Context loadContextServlet(String appBase, String filename) throws Exception {
        String path = appBase + "\\" + filename;
        Context context = new Context();
        String webPath = path + "\\web.xml";
        if (!new File(webPath).exists()) {
            return null;
        }
        context.setContextPath(webPath);
        context.setContextName(filename);
        InputStream inputStream = new FileInputStream(webPath);
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();
        List<Element> servletElementList = rootElement.selectNodes("//servlet");
        for (Element servletElement : servletElementList) {
            String servletName = servletElement.selectSingleNode("servlet-name").getStringValue();
            String servletClass = servletElement.selectSingleNode("servlet-class").getStringValue();
            Element servletMappingEle = (Element) servletElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
            String urlPattern = servletMappingEle.selectSingleNode("url-pattern").getStringValue();

            Wrapper wrapper = new Wrapper();
            // 自定义类加载器，来加载 webapps 目录下的 class
            MyClassLoader myClassLoader = new MyClassLoader();
            Class<?> aClass = myClassLoader.findClass(path, servletClass);
            wrapper.setServlet((Servlet) aClass.getDeclaredConstructor().newInstance());
            wrapper.setUrlPattern(urlPattern);

            context.addWrapper(wrapper);
        }
        return context;
    }


}
